---
title: 依赖倒置原则
description: Dependency Inversion Principle (DIP) - 高层模块不应该依赖低层模块，两者都应该依赖其抽象
sidebar_position: 6
---

## 什么是依赖倒置原则

依赖倒置原则 (Dependency Inversion Principle, DIP) 是 SOLID 原则中的最后一个原则，由 Robert Martin 提出。其核心思想是：

> 高层模块不应该依赖低层模块，两者都应该依赖抽象。  
> 抽象不应该依赖具体实现细节，具体实现细节应该依赖抽象。

依赖倒置原则的目的是降低高层模块对低层模块的依赖，减少代码的耦合性，增强系统的可维护性和灵活性。

## 高层模块与低层模块

在依赖倒置原则中：

- **高层模块**：指调用者，通常负责复杂的业务逻辑
- **低层模块**：指被调用者，通常负责基础操作，如数据访问、网络通信等
- **抽象**：指接口或抽象类，用于定义规范，不包含具体实现

在传统开发中，高层模块直接依赖低层模块，形成了从上到下的依赖关系。而依赖倒置原则要求反转这种依赖关系，让高层模块和低层模块都依赖抽象，从而形成了依赖"倒置"。

## 示例一：消息发送系统

### 不符合依赖倒置原则的设计

```java
public class Notification {
    private MessageSender messageSender;
    
    public Notification() {
        // 直接依赖具体实现
        this.messageSender = new MessageSender();
    }
    
    public void sendMessage(String cellphone, String message) {
        this.messageSender.send(cellphone, message);
    }
}

public class MessageSender {
    public void send(String cellphone, String message) {
        // 具体的短信发送实现
    }
}

// 使用方式
Notification notification = new Notification();
notification.sendMessage("13912345678", "验证码：1234");
```

### 符合依赖倒置原则的设计

```java
// 定义抽象接口
public interface MessageSender {
    void send(String cellphone, String message);
}

// 具体实现类
public class SmsSender implements MessageSender {
    @Override
    public void send(String cellphone, String message) {
        // 发送短信的具体实现
    }
}

public class EmailSender implements MessageSender {
    @Override
    public void send(String cellphone, String message) {
        // 发送邮件的具体实现
    }
}

// 高层模块依赖抽象接口
public class Notification {
    private MessageSender messageSender;
    
    // 通过构造函数注入依赖
    public Notification(MessageSender messageSender) {
        this.messageSender = messageSender;
    }
    
    public void sendMessage(String cellphone, String message) {
        this.messageSender.send(cellphone, message);
    }
}

// 使用方式
MessageSender smsSender = new SmsSender();
Notification notification = new Notification(smsSender);
notification.sendMessage("13912345678", "验证码：1234");
```

在符合依赖倒置原则的设计中：
1. `Notification`（高层模块）不再直接依赖`SmsSender`（低层模块）
2. 两者都依赖`MessageSender`接口（抽象）
3. 依赖的方向"倒置"了 - 低层模块实现了高层模块定义的接口

## 示例二：框架与应用

在实际应用中，依赖倒置原则经常用于指导框架层面的设计。

例如，Tomcat 作为 Java Web 应用的容器：
- Tomcat 是高层模块
- 用户编写的 Web 应用是低层模块
- 两者都依赖 Servlet 规范（抽象）

不是 Tomcat 依赖用户的应用，而是 Tomcat 和用户应用都依赖 Servlet 规范。用户应用实现 Servlet 接口，Tomcat 通过这些接口调用应用程序，这就是依赖的"倒置"。

## 依赖倒置与相关概念的关系

### 依赖倒置原则 vs 控制反转

- **控制反转 (Inversion of Control, IoC)** 是一种设计思想，它将程序的执行流程控制权从程序员反转给框架。
- 控制反转是一个宽泛的概念，可以通过多种方式实现，包括依赖注入、模板方法模式等。
- 依赖倒置原则是一种设计原则，指导如何设计类与类之间的依赖关系。
- 控制反转是依赖倒置原则的一种应用方式。

### 依赖倒置原则 vs 依赖注入

- **依赖注入 (Dependency Injection, DI)** 是一种编程技巧，它不在类内部通过 new 创建依赖对象，而是从外部传入依赖对象。
- 依赖注入是实现依赖倒置原则的一种手段。
- 依赖注入强调"如何传递依赖"，依赖倒置原则强调"依赖什么"。

### 依赖注入框架

依赖注入框架（如 Spring）是自动化依赖注入过程的工具，它可以：
1. 自动创建对象
2. 管理对象的生命周期
3. 自动进行依赖注入

使用依赖注入框架可以简化依赖倒置原则的实现，使开发者专注于业务逻辑。

## 为什么需要依赖倒置

1. **降低耦合度**：通过依赖抽象而非具体实现，降低了模块间的耦合度
2. **提高可扩展性**：可以轻松替换底层模块的实现，而无需修改高层模块
3. **提高可测试性**：可以轻松使用 Mock 对象替代真实依赖，便于单元测试
4. **提高代码稳定性**：抽象接口变化的频率远低于具体实现，依赖抽象可以减少变化带来的影响

## 实践建议

1. 高层模块定义抽象接口，低层模块实现这些接口
2. 使用依赖注入的方式传递依赖
3. 优先使用构造函数注入，确保核心依赖不可变
4. 对于可选依赖，可以使用 setter 注入
5. 考虑使用依赖注入框架简化对象创建和依赖管理
6. 接口设计要保持稳定，避免频繁变更

## 总结

依赖倒置原则是 SOLID 原则中的重要一环，它通过反转传统的依赖关系，使得高层模块和低层模块都依赖于抽象，从而降低系统耦合度，提高系统的可维护性和灵活性。

依赖倒置原则与控制反转、依赖注入有密切关系：
- 依赖倒置原则是一种设计原则，指导"依赖什么"
- 控制反转是一种设计思想，实现程序控制流的反转
- 依赖注入是一种编程技巧，解决"如何传递依赖"
- 依赖注入框架是自动化依赖注入的工具

